package org.jsets.fastboot.util;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.cglib.core.Converter;

import com.google.common.collect.Maps;
/**
 * 
 * bean拷贝
 * <br>
 * 大部分情况下cglib的BeanCopier 性能比 spring的BeanUtils高
 * <br>
 * 与lombok的@Accessors(chain = true)注解冲突
 *
 * @author wangjie (https://github.com/wj596)
 * @date 2021.07.10 21:57
 * @since 0.1
 */
public class BeanCopyUtils {

	private static final Object monitor = new Object();
	private static final ConcurrentHashMap<String, BeanCopier> CACHES = new ConcurrentHashMap<>();

	/**
	 * 拷贝
	 * @param source 源
	 * @param target 目标
	 */
	public static void copy(Object source, Object target) {
		Objects.requireNonNull(source);
		Objects.requireNonNull(target);
		
		genBeanCopier(source.getClass(), target.getClass(), false).copy(source, target, null);
	}

	/**
	 * 拷贝
	 * @param source 源
	 * @param target 目标
	 * @param converter 转换器
	 */
	public static void copy(Object source, Object target, Converter converter) {
		Objects.requireNonNull(source);
		Objects.requireNonNull(target);
		
		genBeanCopier(source.getClass(), target.getClass(), true).copy(source, target, converter);
	}

	private static BeanCopier genBeanCopier(Class<?> srcClazz, Class<?> tgtClazz, boolean useConverter) {
		String key = srcClazz.getName() +"|"+ tgtClazz.getName() + "|" + useConverter;
		BeanCopier beanCopier  = CACHES.get(key);
		if (null!=beanCopier) {
			return beanCopier;
		} 
		
		synchronized (monitor) {
			beanCopier  = CACHES.get(key);
			if (null!=beanCopier) {
				return beanCopier;
			} 
			
			beanCopier = BeanCopier.create(srcClazz, tgtClazz, useConverter);
			CACHES.put(key, beanCopier);
			return beanCopier;
		}
	}
	
	/**
	 * Bean转Map
	 * @param bean Bean
	 */
	public static Map<String, Object> toMap(Object bean) {
		Objects.requireNonNull(bean);
		
		Map<String, Object> map = Maps.newHashMap();
		BeanMap bm = BeanMap.create(bean);
		for (Object key : bm.keySet()) {
			map.put(key+"", bm.get(key));
		}
		return map;
	}
	
}
